home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 2 / AACD 2.iso / AACD / Online / Socks5 / src / server / socket.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-10  |  24.1 KB  |  770 lines

  1. /* Copyright (c) 1995-1999 NEC USA, Inc.  All rights reserved.               */
  2. /*                                                                           */
  3. /* The redistribution, use and modification in source or binary forms of     */
  4. /* this software is subject to the conditions set forth in the copyright     */
  5. /* document ("Copyright") included with this distribution.                   */
  6.  
  7. /*
  8.  * $Id: socket.c,v 1.94.2.2.2.16 1999/03/05 21:52:48 steve Exp $
  9.  */
  10.  
  11. /* This file contains all of the socket creation and configuration functions */
  12. /* that are used by the daemon.  There are a couple of utility functions     */
  13. /* here becuase they deal with the sockets themselves...                     */
  14. #include "socks5p.h"
  15. #include "daemon.h"
  16. #include "validate.h"
  17. #include "msgids.h"
  18. #include "socket.h"
  19. #include "proxy.h"
  20. #include "sema.h"
  21. #include "log.h"
  22. #include "threads.h"
  23. #include "sigfix.h"
  24.  
  25. #if defined(linux) && defined(USE_THREADS)
  26. #undef SIGUSR1
  27. #define SIGUSR1 SIGUNUSED
  28. #endif
  29.  
  30. /* Two server modes (threading & preforking) work in a master/slave mode,    */
  31. /* these macros make decisions based on that status easier to read           */
  32. #define ISMASTER() (!iamachild && (servermode == PREFORKING || servermode == THREADED))
  33. #define ISSLAVE()  (iamachild  && (servermode == PREFORKING || servermode == THREADED))
  34.  
  35. static int acceptor  = 0;  /* the pid of the accepting process if threaded   */
  36. static int nconns    = 0;  /* the number of proxies the daemon has had...    */
  37. static int nchildren = 0;  /* the number of children the daemon has had...   */
  38. static int iamachild = 0;  /* Am I a child, or the parent...                 */
  39. static void *asem = NULL;
  40. static S5IOHandle in = S5InvalidIOHandle;
  41.  
  42. static sig_atomic_t hadfatalsig = 0;  /* Has a sighup/sigusr1 occured?       */
  43. static sig_atomic_t hadsigint   = 0;  /* Has a sigint         occured?       */
  44. static sig_atomic_t hadresetsig = 0;  /* Has a sigusr1        occured?       */
  45.  
  46. /* A function to collect dead children before they become zombies...  Also,  */
  47. /* It keeps track of how many children are around for preforking...          */
  48. /*                                                                           */
  49. /* On a non-posix system, I suppose calling wait3 or waitpid in a signal     */
  50. /* handler (according to APUE) might not be cool.  Someone will have to      */
  51. /* tell me if this is ever the case, and I'll write code so that reaping is  */
  52. /* done elsewhere.                                                           */
  53. static RETSIGTYPE gravedigger(void) {
  54.     int oerrno = errno, wval, wstatus;
  55.     Sigset_t set = SigBlock(SIGCHLD);
  56.  
  57.     for (;;) {
  58. #ifdef HAVE_WAITPID
  59.     wval = waitpid(-1, &wstatus, WNOHANG);
  60. #else
  61.         wval = wait3(&wstatus, WNOHANG, NULL);
  62. #endif
  63.  
  64.     switch (wval) {
  65.         case -1:
  66.         if (errno == EINTR) continue;
  67.         case 0:
  68.         SigUnblock(set);
  69.         errno = oerrno;
  70.         return;
  71.         default:
  72.         if (servermode == THREADED && wval == acceptor) acceptor = 0;
  73.         if (nchildren > 0) nchildren--;
  74.     }
  75.     }
  76. }
  77.  
  78. /* Indicate that we had a sigint, so we exit & clean up later                */
  79. static RETSIGTYPE markdone(void) {
  80.     if (hadresetsig) hadresetsig = 0;
  81.     else hadsigint = 1;
  82. }
  83.  
  84. /* Indicate that we had a sighup, so we can re-read the config file later.   */
  85. static RETSIGTYPE die(void) {
  86.     hadfatalsig = 1;
  87. }
  88.  
  89. /* Indicate that we had a sigusr, so we start a new server.                  */
  90. static RETSIGTYPE reset(void) {
  91.     hadresetsig = 1;
  92. }
  93.  
  94. /* fork(), and do the right thing...the right thing is...handle errors and   */
  95. /* incremenent nchildren...pretty simple...                                  */
  96. static int DoFork(void) {
  97.     int pid;
  98.   
  99.     if (nchildren >= nservers) {
  100.     errno = EAGAIN;
  101.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Total number of children is %d", nchildren);
  102.     return -1;
  103.     }
  104.   
  105.     switch (pid = fork()) {
  106.     case 0:
  107.         Signal(SIGHUP,  SIG_DFL);
  108.         Signal(SIGUSR1, SIG_DFL);
  109.         Signal(SIGINT,  SIG_DFL);
  110.             Signal(SIGTERM, SIG_DFL);
  111.  
  112.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Child: Starting");
  113.         nchildren = 0; 
  114.         nservers = 0; 
  115.         iamachild = 1;
  116.         return 0;
  117.     case -1:
  118.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR,     0, "Fork failed: %m");
  119.         return -1;
  120.     default:
  121.         nchildren++; 
  122.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Parent: %d child%s", nchildren, (nchildren != 1)?"ren":"");
  123.         return pid;
  124.     }
  125. }
  126.  
  127. static int GetBindIntfc(S5NetAddr *bndaddr) {
  128.     u_short bindport = 0;
  129.     char *tmp = NULL, *tmpaddr = NULL, *tmpport = NULL;
  130.  
  131.     if (bindif) tmp = strdup(bindif);
  132.     else {
  133.         MUTEX_LOCK(env_mutex);
  134.         tmp = getenv("SOCKS5_BINDINTFC");
  135.         if (tmp) tmp = strdup(tmp);
  136.         MUTEX_UNLOCK(env_mutex);
  137.     }
  138.  
  139.     if (tmp) {
  140.         if ((tmpport = strchr(tmp, ':'))) {
  141.             *tmpport++ = '\0';
  142.             if (*tmp) tmpaddr = tmp;
  143.         } else {
  144.             if (isdigit((unsigned char)*tmp) && !strchr(tmp, '.')) tmpport = tmp;
  145.             else tmpaddr = tmp;
  146.         }
  147.     }
  148.  
  149.     if (!tmpaddr) {
  150.        memset((char *)bndaddr, 0, sizeof(S5NetAddr));
  151.        bndaddr->sin.sin_family       = AF_INET;
  152.        bndaddr->sin.sin_addr.s_addr  = htonl(INADDR_ANY);
  153.     } else if (lsName2Addr(tmpaddr, bndaddr) < 0 || bndaddr->sin.sin_addr.s_addr == INVALIDADDR ) {
  154.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, " Invalid address %s specified", tmpaddr);
  155.         return -1;
  156.     }
  157.  
  158.     if (tmpport) lsName2Port(tmpport, "tcp", &bindport);
  159.     else lsName2Port("socks", "tcp", &bindport);
  160.  
  161.     if (bindport == INVALIDPORT) bindport = htons(SOCKS_DEFAULT_PORT);
  162.  
  163.     lsAddrSetPort(bndaddr, bindport);
  164.  
  165.     if (tmp) free(tmp);
  166.     return 0;
  167. }
  168.  
  169. static void GetUdpPortRange(void) {
  170.     char *tmp = NULL, *tmpport = NULL;
  171.  
  172.     tmp = getenv("SOCKS5_UDPPORTRANGE");
  173.     if (tmp) tmp = strdup(tmp);
  174.  
  175.     if (tmp) {
  176.     if ((tmpport = strchr(tmp, '-'))) *tmpport++ = '\0';
  177.  
  178.     if (*tmp && isdigit((unsigned char)*tmp)) ludpport = (u_short)atoi(tmp);
  179.     else {
  180.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, " Invalid udp port range %s", tmp);
  181.         free(tmp);
  182.         return;
  183.     }
  184.  
  185.     if (tmpport && *tmpport && isdigit((unsigned char)*tmpport)) hudpport = (u_short)atoi(tmpport);
  186.     free(tmp);
  187.     }
  188. }
  189.  
  190. /* Make a socket with the correct protocol (p), bind it to the right port    */
  191. /* (n, the name or port, the default), and set the function to be called the */
  192. /* socket becomes active (func)...store all this in an fdrec structure for   */
  193. /* convenience's sake...if for any reason something fails and this enry      */
  194. /* should become invalid, the r->fd should be set to -1, so other places     */
  195. /* know to ignore it...                                                      */
  196. static int MakeSocket(int start, S5IOHandle *infd) {
  197.     S5NetAddr bndaddr;
  198.     int on = 1;
  199.  
  200.     if (!start) {
  201.     time_t now = time(NULL);
  202.     char tbuf[1024];
  203.  
  204.     MUTEX_LOCK(lt_mutex);
  205.     strftime(tbuf, sizeof(tbuf), "%c", localtime(&now));
  206.     MUTEX_UNLOCK(lt_mutex);
  207.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_RESTART, "Socks5 restarting at %s", tbuf);
  208.     }
  209.  
  210.     if (*infd != S5InvalidIOHandle) {
  211.     return 0;
  212.     }
  213.  
  214.     if (start) {
  215.         if (GetBindIntfc(&bndaddr) < 0) goto cleanup;
  216.     } 
  217.  
  218.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(12), 0, "Socks5 attempting to run on interface %s:%d", ADDRANDPORT(&bndaddr));
  219.  
  220.     if ((*infd = socket(AF_INET, SOCK_STREAM, 0)) == S5InvalidIOHandle) {
  221.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "Socket failed for %s:%d: %m", ADDRANDPORT(&bndaddr));
  222.     goto cleanup;
  223.     }
  224.  
  225.     if (setsockopt(*infd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(int)) < 0) {
  226.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0,  "Turning on address reuse failed for %s:%d: %m", ADDRANDPORT(&bndaddr));
  227.     goto cleanup;
  228.     }
  229.   
  230.     if (bind(*infd, (ss *)&bndaddr, sizeof(ssi)) < 0) {
  231.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, MSGID_SERVER_SOCKS_BIND, "Bind failed for %s:%d: %m", ADDRANDPORT(&bndaddr));
  232.     goto cleanup;
  233.     }
  234.   
  235.     if (listen(*infd, 5) < 0) {
  236.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "Listen failed for %s:%d: %m", ADDRANDPORT(&bndaddr));
  237.     goto cleanup;
  238.     }
  239.  
  240.     /* If we bound, we're the owner, so put our pid in the pidfile.          */
  241. #ifndef DONT_STORE_PID
  242.     if (start) {
  243.     char abuf[64], *myfl, *ofile = NULL;
  244.     S5IOHandle fd = S5InvalidIOHandle;
  245.     pid_t pid = getpid();
  246.     struct stat sbuf;
  247.       
  248.     MUTEX_LOCK(env_mutex);
  249.     myfl = getenv("SOCKS5_PIDFILE");
  250.     myfl = myfl?strdup(myfl):strdup(SRVPID_FILE);
  251.     MUTEX_UNLOCK(env_mutex);
  252.  
  253.     if ((ofile = malloc(strlen(myfl)+7))) {
  254.         sprintf(ofile, "%s-%d", myfl, (int)ntohs(bndaddr.sin.sin_port));
  255.         free(myfl);
  256.         myfl = ofile;
  257.     } else {
  258.         free(myfl);
  259.         myfl = NULL;
  260.     }
  261.       
  262.     if (myfl) {
  263.         int flags = O_WRONLY | O_CREAT | O_TRUNC;
  264.         /* Open exclusively if the file doesn't exist or if it does, it  */
  265.         /* is a link, and someone else owns it                           */
  266.         if (lstat(myfl, &sbuf) || (S_ISLNK(sbuf.st_mode) && geteuid() != sbuf.st_uid)) flags |= O_EXCL;
  267.         fd = open(myfl, flags, 0644);
  268.     }
  269.  
  270.     if (fd == S5InvalidIOHandle) {
  271.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "Error: Failed to open pid file: %s: %m", myfl?myfl:"(null)");
  272.     } else {
  273.         sprintf(abuf, "%d\n", (int)pid);
  274.         WRITEFILE(fd, abuf, strlen(abuf));
  275.         close(fd);
  276.     }
  277.     
  278.     if (myfl) free(myfl);
  279.     }
  280. #endif
  281.  
  282.     return 0;
  283.     
  284.   cleanup:
  285.     if (*infd != S5InvalidIOHandle) CLOSESOCKET(*infd);
  286.     *infd = S5InvalidIOHandle;
  287.     return -1;
  288. }
  289.  
  290. /* This is called whenever an error occurs that requires a signal to fix.    */
  291. /* If the server is preforking or threaded, the parent begins in this state, */
  292. /* and only children come out of it... If it is normal, this state is        */
  293. /* reached by having a fatal call to accept...                               */
  294. /*                                                                           */
  295. /* Children come here sometimes when they've had fatal signals and need to   */
  296. /* be killed off...                                                          */
  297. static int GetSignals(void *asem, S5IOHandle *infd) {
  298.     Sigset_t set;
  299.  
  300.     if (iamachild) {
  301.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Childs exiting");
  302.         exit(0);
  303.     }
  304.  
  305.     /* Block appropriate signals here so that we don't get interrupted while */
  306.     /* we're forking or setting things up...                                 */
  307.     for (set = SigBlock(SIGHUP); ; ) {
  308.     /* Do our thing if everything is ok...                               */
  309.     if (*infd != S5InvalidIOHandle) {
  310.         switch (servermode) {
  311.         case THREADED:
  312.             if (acceptor == 0) acceptor = DoFork();
  313.             if (iamachild) goto done;
  314.             break;
  315.         case PREFORKING:
  316.             while (DoFork() > 0);
  317.             if (iamachild) goto done;
  318.             break;
  319.         }
  320.     }
  321.  
  322.         if (servermode == THREADED && (acceptor < 0 && errno != EAGAIN)) {
  323.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "server exiting: fork failed");
  324.             hadsigint = 1;
  325.         }
  326.  
  327.     /* Wait for any signal to arrive, esp SIGCHLD and SIGHUP.  SIGHUP    */
  328.     /* will cause a re-read of the config file, everything else: loop.   */
  329.     if (!hadfatalsig && !hadsigint && !hadresetsig && *infd != S5InvalidIOHandle) {
  330.         SigPause();
  331.     }
  332.     
  333.     if (hadsigint) {
  334.         time_t now = time(NULL);
  335.         char tbuf[1024];
  336.  
  337.         hadresetsig = 0;
  338.             kill(-getpid(), SIGINT);
  339.         while (nchildren > 0) SigPause();
  340.         if (ISMASTER()) { semdestroy(asem); }
  341.  
  342.         MUTEX_LOCK(lt_mutex);
  343.         strftime(tbuf, sizeof(tbuf), "%c", localtime(&now));
  344.         MUTEX_UNLOCK(lt_mutex);
  345.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_INFO, MSGID_SERVER_STOP, "Socks5 Exiting at: %s", tbuf);
  346.         exit(0);
  347.     }
  348.  
  349.     if (!hadfatalsig && !hadresetsig) {
  350.             /* SIGCHLD received...                                           */
  351.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Parent reaped? (%d child%s)", nchildren, (nchildren != 1)?"ren":"");
  352.             continue;
  353.     }
  354.     
  355.     if (hadfatalsig) {
  356.         char *tmp = NULL;
  357.  
  358.         if (servermode == PREFORKING || servermode == THREADED) {
  359.         /* Kill whoever is accepting (all of them if preforking) */
  360.         /* and reset the semaphore...                            */
  361.         hadresetsig = 1;
  362.                 kill(-getpid(), SIGINT);
  363.         if (ISMASTER()) { semreset(asem, 1); } 
  364.         acceptor = 0;
  365.         }
  366.  
  367.         ReadConfig();
  368.  
  369.         if ((tmp = getenv("SOCKS5_TIMEOUT")) && *tmp) idletimeout = atoi(tmp);
  370.         GetUdpPortRange();
  371.     }
  372.  
  373.     if (hadresetsig && servermode == THREADED) acceptor = 0;
  374.  
  375.     hadfatalsig = 0;
  376.     hadresetsig = 0;
  377.  
  378.         if (servermode == NORMAL) break;
  379.     }
  380.  
  381.   done:
  382.     SigUnblock(set);
  383.     return 0;
  384. }
  385.  
  386. static void DoWork(S5IOHandle sd) {
  387.     int eval;
  388.  
  389.     if (servermode == NORMAL && (eval = DoFork()) != 0) {
  390.     CLOSESOCKET(sd);
  391.     if (eval > 0 || errno == EAGAIN) return;
  392.     exit(-1);
  393.     }
  394.  
  395.     eval = HandlePxyConnection(sd);
  396.  
  397.     if (servermode == PREFORKING || servermode == THREADED) {
  398.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(10), 0, "Accept: Done with connection...");
  399.     return;
  400.     }
  401.     
  402.     exit(eval);
  403. }
  404.  
  405. static int AcceptConnection(int sd, struct sockaddr *sa, int *alen) {
  406.     struct timeval acceptout;
  407.     static fd_set rfds;
  408.  
  409.     if (servermode != THREADED) return accept(sd, sa, alen);
  410.  
  411.     for (;;) {
  412.         acceptout.tv_sec = 180;
  413.         acceptout.tv_usec = 0;
  414.  
  415.         FD_SET(sd, &rfds);
  416.         switch (select(sd+1, &rfds, NULL, NULL, &acceptout)) {
  417.         case -1:
  418.         if (errno == EINTR || errno == EAGAIN) continue;
  419.         return -1;
  420.         case 0:
  421.         errno = ETIMEDOUT;
  422.         return -1;
  423.         default:
  424.             if (FD_ISSET(sd, &rfds)) {
  425.             FD_CLR(sd, &rfds);
  426.                 return accept(sd, sa, alen);
  427.         }
  428.     }
  429.     return -1;
  430.     }
  431. }
  432.  
  433. static void WaitSecond(void) {
  434.     static int ud = 0;
  435.     struct timeval waitout;
  436.     static fd_set rfds;
  437.  
  438.     if (ud < 0) {
  439.         sleep(1);
  440.         return;
  441.     } else if (ud == 0) {
  442.         if ((ud = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
  443.             sleep(1);
  444.             return;
  445.         }
  446.     }
  447.  
  448.     FD_SET(ud, &rfds);
  449.     waitout.tv_sec = 0;
  450.     waitout.tv_usec = 100;
  451.     select(ud+1, &rfds, NULL, NULL, &waitout);
  452. }
  453.  
  454. #if defined(USE_THREADS) && defined(HAVE_PTHREAD_H)
  455. static void DoThreadWork(S5IOHandle sd) {
  456.     int len, aerrno = 1;
  457.     S5NetAddr source;
  458.     S5IOHandle afd = sd;
  459.  
  460.     for (;;) {
  461.     DoWork(afd);
  462.  
  463.     MUTEX_LOCK(conn_mutex);
  464.     nconns--;
  465.         if (hadresetsig) {
  466.             nservers--;
  467.             MUTEX_UNLOCK(conn_mutex);
  468.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "DoThreadWork: I have to go.");
  469.             THREAD_EXIT(0);
  470.     }
  471.     MUTEX_UNLOCK(conn_mutex);
  472.  
  473.     while (1) {
  474.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "DoThreadWork: Acquiring mutex/semaphore");
  475.         MUTEX_LOCK(accept_mutex);
  476.         if (semacquire(asem)) {
  477.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(2), 0, "DoThreadWork: Semaphore failure.");
  478.         MUTEX_UNLOCK(accept_mutex);
  479.         exit(-1);
  480.         }
  481.  
  482.         if (hadresetsig) {
  483.                 nservers--;
  484.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "DoThreadWork: Releasing semaphore/mutex");
  485.             semrelease(asem);
  486.             MUTEX_UNLOCK(accept_mutex);
  487.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "DoThreadWork: I have to go.");
  488.             THREAD_EXIT(0);
  489.         }
  490.  
  491.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "DoThreadWork: Waiting on accept");
  492.  
  493.         len = sizeof(source);
  494.         memset(&source, 0, len);
  495.         if ((afd = AcceptConnection(in, &source.sa, &len)) == S5InvalidIOHandle) {
  496.          nservers--;
  497.         if (errno != ETIMEDOUT) {
  498.             hadresetsig = 1;
  499.             aerrno = -1;
  500.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_WARNING, 0, "DoThreadWork: accept failure. %m");
  501.         } else {
  502.             aerrno = 0;
  503.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(2), 0, "DoThreadWork: accept timedout");
  504.         }
  505.         }
  506.  
  507.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "DoThreadWork: Releasing semaphore/mutex");
  508.         semrelease(asem);
  509.         MUTEX_UNLOCK(accept_mutex);
  510.  
  511.         if (aerrno <= 0) {
  512.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "DoThreadWork: Exiting");
  513.         THREAD_EXIT(aerrno);
  514.         }
  515.  
  516.         MUTEX_LOCK(conn_mutex);
  517.         nconns++;
  518.         MUTEX_UNLOCK(conn_mutex);
  519.         break;
  520.     }
  521.     }
  522. }
  523. #endif
  524.  
  525. /* Get a connection from our input socket.  Then based on the socket's       */
  526. /* data, handle the connection correctly, and go on waiting for more         */
  527. /* connetions...                                                             */
  528. static void GetNetConnection(void) {
  529.     Sigset_t set = SigBlock(ISMASTER()?SIGUSR1:SIGHUP);
  530.     S5IOHandle afd;
  531.     S5NetAddr source;
  532.     int aerrno;
  533.     char *tmp = NULL;
  534.  
  535. #if !defined(USE_THREADS) || !defined(HAVE_PTHREAD_H)
  536.     if (servermode == THREADED) {
  537.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Warning: Attempt to run server in threaded mode when threads were not a compile time option");
  538.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Warning: Running as a normal standalone server");
  539.         SigUnblock(set);
  540.         set = SigBlock(SIGHUP);
  541.     servermode = NORMAL;
  542.     }
  543. #endif
  544.  
  545.     Signal(SIGUSR1, reset);
  546.     Signal(SIGHUP,  die);
  547.     Signal(SIGCHLD, gravedigger);
  548.  
  549.     ReadConfig();
  550.  
  551.     if (MakeSocket(1, &in) < 0) {
  552.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "Accept: Failed to make listening socket");
  553.         exit(-1);
  554.     }
  555.  
  556.     if ((tmp = getenv("SOCKS5_TIMEOUT")) && *tmp) idletimeout = atoi(tmp);
  557.     GetUdpPortRange();
  558.  
  559.     Signal(SIGINT,  markdone);
  560.     Signal(SIGTERM, markdone);
  561.  
  562.     if (ISMASTER()) {
  563.     asem = semcreate(1);
  564.     GetSignals(asem, &in);
  565.     }
  566.  
  567.     if (ISMASTER()) {
  568.     S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(0), 0, "Error: Master reached slave code in GetNetConnection");
  569.     exit(EXIT_ERR);
  570.     }
  571.  
  572.     if (ISSLAVE()) {
  573.     hadsigint = 0;
  574.     hadresetsig = 0;
  575.     }
  576.  
  577.     for (;;) {
  578.     int len = sizeof(S5NetAddr);
  579.  
  580.     /* If an important signal has arrived or the acc fd is corrupted,    */
  581.     /* got into the signal waiting state (and possibly exit - if child). */
  582.     if (hadfatalsig || hadsigint || in == S5InvalidIOHandle) {
  583.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(2), 0, "Accept: Processing exception");
  584.         GetSignals(asem, &in);
  585.         hadfatalsig = 0;
  586.     }
  587.     
  588.     if (SigPending(ISSLAVE()?SIGUSR1:SIGHUP)) {
  589.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(2), 0, "Accept: Waiting for a pending fatal signal...");
  590.         while (!hadfatalsig) SigPause();
  591.         continue;
  592.     }
  593.     
  594.     /* Try to accept a connection.  We may receive a signal (HUP or      */
  595.     /* USR1) here, if that happens, accept should return -1, with errno  */
  596.     /* set to EINTR.  We'll handle that later, after we release the      */
  597.     /* semaphore.  If we've already received a signal, don't bother      */
  598.     /* accepting the connection, just set pri->in to -1 and errno to     */
  599.     /* EINTR, to simulate having received the signal here.               */
  600.     if (hadfatalsig) {
  601.         afd    = S5InvalidIOHandle;
  602.         aerrno = EINTR;
  603.     } else {
  604.         /* For some reason (thanks to Rich Stevens for pointing this     */
  605.         /* out), System 5 is unhappy about a bunch of people calling     */
  606.         /* accept.  So we'll add some locks around it to synchronize     */
  607.         /* access...                                                     */
  608.         if (ISSLAVE()) {
  609.         SigUnblock(set);
  610.  
  611.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Accept: Acquiring mutex/semaphore");
  612.         if (servermode == THREADED) {
  613.             if (hadresetsig) {
  614.             kill(getppid(), SIGUSR1);
  615.                 S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(2), 0, "Accept: Main thread exiting.");
  616.             THREAD_EXIT(-1);
  617.             }
  618.  
  619.             MUTEX_LOCK(accept_mutex);
  620.         }
  621.  
  622.         if (semacquire(asem)) {
  623.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(2), 0, "Accept: Semaphore failure.");
  624.             /* If semaphore fails, something is very wrong with this */
  625.             /* process. We better exit now...                        */
  626.                 if (servermode == THREADED) {
  627.             MUTEX_UNLOCK(accept_mutex);
  628.             exit(-1);
  629.             } else { 
  630.             CLOSESOCKET(in);
  631.             exit(-1);
  632.             }
  633.         }
  634.  
  635.         if (servermode == THREADED) {
  636.             if (hadresetsig) {
  637.                 semrelease(asem);
  638.                 MUTEX_UNLOCK(accept_mutex);
  639.             kill(getppid(), SIGUSR1);
  640.                 S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(2), 0, "Accept: Main thread exiting.");
  641.             THREAD_EXIT(-1);
  642.             }
  643.             
  644.             MUTEX_LOCK(conn_mutex);
  645.             if (nconns < nservers) {
  646.             MUTEX_UNLOCK(conn_mutex);
  647.                 semrelease(asem);
  648.             MUTEX_UNLOCK(accept_mutex);
  649.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Accept: Main thread yields (%d:%d)", nconns, nservers);
  650.             WaitSecond();
  651.             continue;
  652.             }
  653.             MUTEX_UNLOCK(conn_mutex);
  654.  
  655.             if (nservers >= nthreads) {
  656.             hadresetsig = 1;
  657.                 semrelease(asem);
  658.             MUTEX_UNLOCK(accept_mutex);
  659.             kill(getppid(), SIGUSR1);
  660.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Accept: Thread(%d) is full", nservers);
  661.             THREAD_EXIT(0);
  662.             }
  663.         }
  664.  
  665.         set = SigBlock((servermode == NORMAL)?SIGHUP:SIGUSR1);
  666.         }
  667.  
  668.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Accept: Waiting on accept or a signal");
  669.  
  670.         SigUnblock(set);
  671.         if ((afd = AcceptConnection(in, &source.sa, &len)) == S5InvalidIOHandle) aerrno = errno;
  672.         set = SigBlock((servermode == NORMAL)?SIGHUP:SIGUSR1);
  673.     }
  674.  
  675.     /* Since we've got the connection, release the semaphore.            */
  676.     /*                                                                   */
  677.     /* We don't have to worry about releasing a reset semaphore, if we   */
  678.     /* received a signal in the meantime, since semreset *should* create */
  679.     /* a whole new semaphore, so we'll be releasing one which no one     */
  680.     /* else looks at anymore.                                            */
  681.     if (ISSLAVE()) {
  682.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Accept: Releasing semaphore/mutex");
  683.         semrelease(asem);
  684.         if (servermode == THREADED) MUTEX_UNLOCK(accept_mutex);
  685.     }
  686.  
  687.     /* Do the work according to the protocol to be passed on pri->in.    */
  688.     /* When we're done, clean things up so we can do it all again...     */
  689.     if (afd == S5InvalidIOHandle) {
  690.         /* We have to make sure the error wasn't too serious.  If it     */
  691.         /* was, quit, unless we are the parent, in which caes we we wait */
  692.         /* for a HUP to tell us things are fixed.                        */
  693.         if (aerrno == EINTR || aerrno == EAGAIN || aerrno == ETIMEDOUT) continue;
  694.  
  695.         errno = aerrno;
  696.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, MSGID_SERVER_SOCKS_ACCEPT, "Accept: Accept failed: %m");
  697.  
  698.         if (servermode != THREADED) close(in);
  699.         exit(-1);
  700.     } else if (servermode == THREADED) {
  701. #if defined(USE_THREADS) && defined(HAVE_PTHREAD_H)
  702.         THREAD_T tid;
  703.         ATTR_T attr;
  704.         sigset_t set, oset;
  705.  
  706.         sigemptyset(&set);
  707.  
  708.             THREAD_ATTR_INIT(attr);
  709.             THREAD_ATTR_SETSTACKSIZE(&attr, 51200);
  710.             THREAD_ATTR_SETSCOPE(attr, PTHREAD_SCOPE_SYSTEM);
  711.             THREAD_ATTR_SETDETACHSTATE(attr, PTHREAD_CREATE_DETACHED);
  712.             THREAD_SIGMASK(SIG_BLOCK, set, oset);
  713.  
  714.             if (THREAD_CREATE(&tid, attr, (void *(*)P((void *)))DoThreadWork, (void *)afd) < 0) {
  715.         hadresetsig = 1;
  716.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_ERROR, 0, "Accept: Thread creation failed: %m");
  717.         kill(getppid(), SIGUSR1);
  718.         close(afd);
  719.         S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(2), 0, "Accept: Main thread exiting.");
  720.         THREAD_EXIT(-1);
  721.         } else {
  722.         THREAD_DETACH(tid);
  723.         THREAD_SIGMASK(SIG_UNBLOCK, set, oset);
  724.  
  725.         MUTEX_LOCK(conn_mutex);
  726.         nconns++;
  727.         nservers++;
  728.         if (nconns >= nservers && nservers >= nthreads) {
  729.             hadresetsig = 1;
  730.             MUTEX_UNLOCK(conn_mutex);
  731.             kill(getppid(), SIGUSR1);
  732.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Accept: Thread(%d) is full", nservers);
  733.             THREAD_EXIT(0);
  734.         } else {
  735.             MUTEX_UNLOCK(conn_mutex);
  736.             S5LogUpdate(S5LogDefaultHandle, S5_LOG_DEBUG(15), 0, "Accept: New thread(%d) created", nservers);
  737.         }
  738.         }
  739. #endif
  740.         afd = S5InvalidIOHandle;
  741.     } else {
  742.         if (servermode == SINGLESHOT) CLOSESOCKET(in);
  743.         DoWork(afd);
  744.     }
  745.     }
  746. }
  747.  
  748. /* Setup a connection which has already been set up for us by inetd.         */
  749. /* Basically, we're just filling in the right structures and calling the     */
  750. /* work function, HandleProxyConnection...                                   */
  751. void GetStdioConnection(void) {
  752.     char *tmp = NULL;
  753.  
  754.     ReadConfig();
  755.  
  756.     if ((tmp = getenv("SOCKS5_TIMEOUT")) && *tmp) idletimeout = atoi(tmp);
  757.     GetUdpPortRange();
  758.  
  759.     fclose(stdout);
  760.     fclose(stderr);
  761.     DoWork(STDIN_FILENO);
  762. }
  763.  
  764. void GetConnection() {
  765.     Signal(SIGPIPE, SIG_IGN);
  766.  
  767.     if (servermode == INETD) GetStdioConnection();
  768.     else                     GetNetConnection();
  769. }
  770.